package com.hoo.permission.sdk.server.service.impl;

import com.hoo.common.model.Page;
import com.hoo.permission.sdk.server.dao.ISysRoleUserDao;
import com.hoo.permission.sdk.server.dao.ISysUserDao;
import com.hoo.permission.sdk.server.dao.ISysUserResourceDao;
import com.hoo.permission.sdk.server.domain.dto.SysUserDto;
import com.hoo.permission.sdk.server.domain.model.OperationType;
import com.hoo.permission.sdk.server.domain.pojo.SysUserPo;
import com.hoo.permission.sdk.server.domain.entity.SysUser;
import com.hoo.permission.sdk.server.domain.model.UserSex;
import com.hoo.permission.sdk.server.domain.model.UserStatus;
import com.hoo.permission.sdk.server.service.ISysUserService;
import com.hoo.permission.sdk.web.config.SafeConfig;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.*;

/**
 * 用户服务实现
 *
 * @author hank
 * @create 2020-07-01 下午10:36
 **/
@Service
public class SysUserServiceImpl implements ISysUserService {

    @Autowired SafeConfig safeConfig;

    @Autowired ISysUserDao userDao;

    @Autowired ISysRoleUserDao roleUserDao;

    @Autowired ISysUserResourceDao userResourceDao;

    @Override
    public Page query(Page page, Map<String, Object> params) {
        if(page == null) {
            page = new Page();
        }
        userDao.query(page, params);
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysUser save(SysUserPo userDto) {
        String password = userDto.getPassword();
        if(StringUtils.isEmpty(password)) {
            password = safeConfig.getUserPassword();
        }
        userDto.setId(null);
        SysUser user = userDto;
        user.setSalt(DigestUtils.md5Hex(UUID.randomUUID().toString() + System.nanoTime() + UUID.randomUUID().toString()));
        user.setStatus(userDto.getStatus() == null ? UserStatus.VALID : userDto.getStatus());
        user.setPassword(passwordEncoder(password, user.getSalt()));
        userDao.add(user);
        saveUserRoles(user.getId(), userDto.getRoleIds(), OperationType.ADD);
        saveUserResource(user.getId(), userDto.getResourceIds(), OperationType.ADD);
        return user;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysUser update(SysUserPo userDto) {
        SysUser user = userDao.get(userDto.getId());
        if(user != null) {
            if (userDto.getStatus() != null && Arrays.asList(new Integer[]{UserStatus.DISABLED, UserStatus.VALID, UserStatus.LOCKED}).indexOf(userDto.getStatus()) > -1) {
                user.setStatus(userDto.getStatus());
            }
            user.setEmail(userDto.getEmail());
            user.setMobile(userDto.getMobile());
            user.setNickname(userDto.getNickname());
            if(Arrays.asList(new String[]{UserSex.UNKNOW, UserSex.MALE, UserSex.FEMALE}).indexOf(userDto.getSex()) > -1) {
                user.setSex(userDto.getSex());
            }
            userDao.update(user);
            // 判断用户角色、资源是否变更，若变更再执行以下逻辑
            List<Long> roleIds = userDto.getRoleIds() == null ? new ArrayList<>() : userDto.getRoleIds();
            List<Long> resourceIds = userDto.getResourceIds() == null ? new ArrayList<>() : userDto.getResourceIds();
            List<Long> curResourceIds = userResourceDao.getResourcesByUserId(userDto.getId());
            List<Long> curRoleIds = roleUserDao.getRoleIds(userDto.getId());
            if (curResourceIds != null && resourceIds != null) {
                curResourceIds.sort(sort);
                resourceIds.sort(sort);
                if(!curResourceIds.equals(resourceIds)) {
                    saveUserResource(user.getId(), resourceIds, OperationType.UPDATE);
                }
            } else {
                saveUserResource(user.getId(), resourceIds, OperationType.UPDATE);
            }
            if(curRoleIds != null && roleIds != null) {
                curRoleIds.sort(sort);
                roleIds.sort(sort);
                if(!curRoleIds.equals(roleIds)) {
                    saveUserRoles(user.getId(), roleIds, OperationType.UPDATE);
                }
            } else {
                saveUserRoles(user.getId(), roleIds, OperationType.UPDATE);
            }
        }
        return user;
    }

    @Override
    public boolean updateStatus(Long id, int status) {
        if(id == null || Arrays.asList(new Integer[]{UserStatus.DISABLED, UserStatus.VALID, UserStatus.LOCKED}).indexOf(status) == -1) {
            throw new IllegalArgumentException("参数不合法");
        }
        return userDao.updateStatus(id, status);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(String username) {
        SysUser user = this.getByUserName(username);
        if(user != null) {
            userDao.delete(user);
            saveUserRoles(user.getId(), new ArrayList<>(), OperationType.DELETE);
            saveUserResource(user.getId(), new ArrayList<>(), OperationType.DELETE);
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean batchDelete(List<Long> ids) {
        if(ids == null || ids.size() == 0) {
            return false;
        }
        Map<String,Object> params = new HashMap<String,Object>(){{
            put("ids", ids);
        }};
        List<SysUserDto> users = userDao.findAll(params);
        for(SysUser user : users) {
            userDao.delete(user);
            saveUserRoles(user.getId(), new ArrayList<>(), OperationType.DELETE);
            saveUserResource(user.getId(), new ArrayList<>(), OperationType.DELETE);
        }
        return true;
    }

    @Override
    public SysUser getByUserName(String username) {
        return userDao.getByUsername(username);
    }

    @Override
    public SysUser get(Serializable id) {
        return userDao.get(id);
    }

    @Override
    public String passwordEncoder(String credentials, String salt) {
        Object object =
                StringUtils.isEmpty(salt) ?
                new SimpleHash(safeConfig.getHashAlgorithmName(), credentials) :
                new SimpleHash(safeConfig.getHashAlgorithmName(), credentials, salt, safeConfig.getHashIterations());
        return object.toString();
    }

    @Override
    public void changePassword(String username, String oldPassword, String newPassword) {
        SysUser user = this.getByUserName(username);
        if(user != null && user.getPassword().equals(oldPassword)) {
            user.setPassword(passwordEncoder(newPassword, user.getSalt()));
            userDao.update(user);
        }
    }

    @Override
    public void resetPassword(String username, String newPassword) {
        SysUser user = this.getByUserName(username);
        if(user != null) {
            user.setPassword(passwordEncoder(newPassword, user.getSalt()));
            userDao.update(user);
        }
    }

    private void saveUserRoles(Long userId, List<Long> roleIds, OperationType type) {
        // 获取当前用户的角色资源信息，若一致，则不再后续执行，否则先删除再新增, 这个逻辑处理交给上一层 修改 时
        if (OperationType.DELETE.equals(type) || OperationType.UPDATE.equals(type)) {
            roleUserDao.deleteByUserId(userId);
        }
        if (roleIds != null && !OperationType.DELETE.equals(type)) {
            roleUserDao.add(userId, roleIds);
        }
    }

    private void saveUserResource(Long userId, List<Long> resourceIds, OperationType type) {
        // 获取当前用户的独立资源信息，若一致，则不再后续执行，否则先删除再新增
        if (OperationType.DELETE.equals(type) || OperationType.UPDATE.equals(type)) {
            userResourceDao.deleteByUserId(userId);
        }
        if(resourceIds != null && !OperationType.DELETE.equals(type)) {
            userResourceDao.add(userId, resourceIds);
        }
    }

    private Comparator sort = new Comparator<Long>() {
        @Override
        public int compare(Long o1, Long o2) {
            return o1.compareTo(o2);
        }
    };
}
